package com.hmall.item.service.impl;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.item.domain.dto.ItemDTO;
import com.hmall.item.domain.dto.ItemDoc;
import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.domain.query.ItemPageQuery;
import com.hmall.item.mapper.ItemMapper;
import com.hmall.item.service.IItemService;
import com.hmall.item.util.ItemSyncEsUtil;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 商品表 服务实现类
 * </p>
 *
 * @author 虎哥
 */
@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements IItemService {
    @Autowired
    private ItemSyncEsUtil itemSyncEsUtil;
    @Value("${server.port}")
    private Integer port;
    @Value("${item.pageSize}")
    private Integer pageSize;
    @Value("${item.suggestionCount}")
    private Integer suggestionCount;

    private static void collection(ItemPageQuery query, BoolQueryBuilder boolQuery) {
        if (StrUtil.isNotBlank(query.getKey())) {
            boolQuery.must(QueryBuilders.matchQuery("name", query.getKey()));
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        if (StrUtil.isNotBlank(query.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        if (StrUtil.isNotBlank(query.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }
        if (query.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        if (query.getMaxPrice() != null && query.getMaxPrice() != 10000) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        } else if (query.getMaxPrice() != null && query.getMaxPrice() == 10000) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lt(query.getMaxPrice()));
        }
    }

    // 判断query的参数是否都为空
    private static boolean isNotEmpty(ItemPageQuery query) {
        return StrUtil.isNotBlank(query.getKey()) ||
                StrUtil.isNotBlank(query.getBrand()) ||
                StrUtil.isNotBlank(query.getCategory()) ||
                query.getMinPrice() != null ||
                query.getMaxPrice() != null;
    }

    @Override
    public void deductStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
        boolean r = false;
        try {
            r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
        } catch (Exception e) {
            log.error("更新库存异常", e);
            throw new BizIllegalException("库存不足！");
        }
        if (!r) {
            throw new BizIllegalException("库存不足！");
        }
    }

    @Override
    public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
        List<ItemDTO> dtoList = BeanUtils.copyList(listByIds(ids), ItemDTO.class);
        dtoList.forEach(dto -> dto.setName(dto.getName() + "-" + port));
        return dtoList;
    }

    @Override
    public void addStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.hmall.item.mapper.ItemMapper.addStock";
        executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
    }

    /**
     * 搜索
     *
     * @param query
     * @return
     * @throws Exception
     */
    @Override
    public PageDTO<ItemDoc> searchQuery(ItemPageQuery query) throws Exception {
        // 1.准备Request
        SearchRequest request = new SearchRequest("items");
        // 2.准备DSL
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 搜索条件
        // 搜索条件
        collection(query, boolQuery);
        // 广告加权
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                // 基本查询条件
                boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                // 过滤条件
                                QueryBuilders.termQuery("isAD", true),
                                // 算分函数
                                ScoreFunctionBuilders.weightFactorFunction(100)
                        )
                }
        ).boostMode(CombineFunction.SUM);
        // 分页查询 + 高亮
        request.source()
                .trackScores(true)
                .query(functionScoreQueryBuilder)
                .highlighter(
                        SearchSourceBuilder.highlight()
                                .field("name").requireFieldMatch(false))
                .from((query.getPageNo() - 1) * query.getPageSize())
                .size(query.getPageSize());
        // 3.排序
        if (StrUtil.isNotBlank(query.getSortBy())) {
            request.source().sort(query.getSortBy(), query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        }
        PageDTO<ItemDoc> objectPageDTO = itemSyncEsUtil.requestAndParseResponseResult(request, ItemDoc.class);

        // 修复空指针异常
        if (ObjUtil.isNotEmpty(objectPageDTO) && ObjUtil.isNotNull(objectPageDTO.getTotal())) {
            objectPageDTO.setPages(
                    Long.valueOf(
                            objectPageDTO.getTotal() / query.getPageSize()
                                    + (objectPageDTO.getTotal() % query.getPageSize() != 0 ? 1 : 0)));
        }
        return objectPageDTO;
    }

    /**
     * 聚合
     *
     * @param query
     * @return
     * @throws Exception
     */

    @Override
    public Map<String, List<String>> filters(ItemPageQuery query) throws Exception {
        // 1.准备Request
        SearchRequest request = new SearchRequest("items");
        // 2.准备DSL
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 搜索条件
        if (isNotEmpty(query)) {
            collection(query, boolQuery);
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        request.source()
                .query(boolQuery)
                .size(0);
        request.source().aggregation(
                AggregationBuilders.terms("categoryAgg").field("category").size(pageSize)
        );
        request.source().aggregation(
                AggregationBuilders.terms("brandAgg").field("brand").size(pageSize)
        );
        List<String> strings = itemSyncEsUtil.parseAggResults(request, "brandAgg", null);
        List<String> strings1 = itemSyncEsUtil.parseAggResults(request, "categoryAgg", null);
        return Map.of("brand", strings, "category", strings1);
    }

    /**
     * 3、自动补全查询
     *
     * @param key
     * @return
     */
    @Override
    public List<String> getSuggestion(String key) throws IOException {
        // 1.准备Request
        SearchRequest request = new SearchRequest("items");
        // 2.准备DSL
        request.source()
                .suggest(
                        new SuggestBuilder().addSuggestion("mySuggest",
                                SuggestBuilders
                                        .completionSuggestion("suggestion")
                                        .prefix(key)
                                        .skipDuplicates(true)
                                        .size(suggestionCount)
                        )
                );
        // 3.发送请求
        SearchResponse response = itemSyncEsUtil.getResponse(request);
        // 4.处理响应
        Suggest suggest = response.getSuggest();
        CompletionSuggestion mySuggest = suggest.getSuggestion("mySuggest");
        // 4.2.获取options
        List<String> resultList = new ArrayList<>();
        for (Suggest.Suggestion.Entry.Option entry : mySuggest.getOptions()) {
            String keyWord = entry.getText().string();
            resultList.add(keyWord);
        }
        return resultList;
    }
}
